package gapidt.plugin;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.PartInitException;

import gapidt.analysis.ASTUtils;
import gapidt.analysis.APIDesignAnalysisResult;
import gapidt.analysis.DesignProblem;
import gapidt.analysis.APIDesignAnalyzer;
import gapidt.analysis.detectors.AmbiguousNamesDetector;
import gapidt.analysis.detectors.InappropriateOrLackingCommentsDetector;
import gapidt.analysis.detectors.LowCohesiveMethodsDetector;
import gapidt.analysis.detectors.UndercoverFaultInformationDetector;
import gapidt.analysis.detectors.WhateverTypesDetector;
import gapidt.common.Locale;
import gapidt.model.ErrorByType;
import gapidt.plugin.views.AnalysisResultsTableView;
import gapidt.plugin.views.AnalysisResultsView;

public class AnalysisResultsManager{
	
	protected List<APIDesignAnalysisResult<?>> resultsList = new ArrayList<APIDesignAnalysisResult<?>>(); 
	protected Map<Object, APIDesignAnalysisResult<?>> results = new HashMap<Object, APIDesignAnalysisResult<?>>();
	protected Map<Object, IEditorPart> resultsEditors = new HashMap<Object, IEditorPart>();
	protected static AnalysisResultsManager instance = null;
	protected List<ErrorByType> designProblemsBytype = new ArrayList<ErrorByType>();

	// Singleton
	protected AnalysisResultsManager(){ }
	
	protected APIDesignAnalyzer getAnalyzer(){
		APIDesignAnalyzer analyzer = new APIDesignAnalyzer();
		analyzer.addDetector(new InappropriateOrLackingCommentsDetector());
    	analyzer.addDetector(new AmbiguousNamesDetector());
		analyzer.addDetector(new UndercoverFaultInformationDetector());
		analyzer.addDetector(new LowCohesiveMethodsDetector());
		analyzer.addDetector(new WhateverTypesDetector());
		return analyzer;
	}
	
	public static AnalysisResultsManager getInstance(){
		if(instance == null)
			instance = new AnalysisResultsManager();
		return instance;
	}
	
	public <T> APIDesignAnalysisResult<T> analyzeElement(T element, IEditorPart editor){
		
		ASTUtils.clearCache();
		
		APIDesignAnalysisResult<T> result = getAnalyzer().analyzeDesign(element);
		addResult(element, result, editor);
		showResult(result);

		return result;
	}

	protected <T> void addResult(T element, APIDesignAnalysisResult<T> result, IEditorPart editor){
		
		if(results.get(element) != null){
			int index = 0;
			for(APIDesignAnalysisResult<?> r : resultsList){
				if(r.getSubject().equals(element)){
					resultsList.add(index, result);
					resultsList.remove(index+1);
					break;
				}
				index++;
			}
		}else
			resultsList.add(result);
		results.put(element, result);
		if(editor != null)
			resultsEditors.put(element, editor);
	}
	
	public void removeResult(DesignProblem<?> designProblem){
		if(designProblem != null)
			removeResult(designProblem.getAnalysisResult().getRootAnalysisResult());
	}
	
	public void removeResult(APIDesignAnalysisResult<?> result){
		if(result != null){
			result = result.getRootAnalysisResult();
			resultsList.remove(result);
			for(Object subject : results.keySet())
				if(results.get(subject).equals(result)){
					results.remove(subject);
					resultsEditors.remove(subject);
					return;
				}
		}
	}
	
	public void clearResults(){
		results.clear();
		resultsEditors.clear();
		resultsList.clear();
		designProblemsBytype.clear();
	}
	
	public Collection<APIDesignAnalysisResult<?>> getResults(){
		return resultsList;
	}
	
	public IEditorPart getResultEditor(DesignProblem<?> designProblem, boolean forceOpen){
		if(designProblem != null){
			APIDesignAnalysisResult<?> rootResult = (APIDesignAnalysisResult<?>) designProblem.getAnalysisResult().getRootAnalysisResult();
			return getResultEditor(rootResult, forceOpen);
		}
		return null;
	}
	
	public IEditorPart getResultEditor(APIDesignAnalysisResult<?> result, boolean forceOpen){
		if(result != null){
			IEditorPart editor = resultsEditors.get(result.getRootAnalysisResult().getSubject());
			if(result.getSubject() instanceof IJavaElement && (editor == null || JavaUI.getWorkingCopyManager().getWorkingCopy(editor.getEditorInput()) == null) && forceOpen){
				editor = openInEditor((IJavaElement) result.getSubject());
				if(editor != null)
					resultsEditors.put(result.getSubject(), editor);
			}
			return editor;
		}
		return null;
	}
	
	protected IEditorPart openInEditor(IJavaElement element){
		try {
			return JavaUI.openInEditor(element, true, true);
		} catch (PartInitException e) {
			e.printStackTrace();
		} catch (JavaModelException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public void showResult(APIDesignAnalysisResult<?> result){
		WorkbenchUtils.showView(AnalysisResultsView.ID);
		AnalysisResultsView resultView = (AnalysisResultsView) WorkbenchUtils.getView(AnalysisResultsView.ID);
		if(resultView != null){
			resultView.refresh();
			resultView.getViewer().collapseAll();
			resultView.getViewer().expandToLevel(result, AbstractTreeViewer.ALL_LEVELS);
		}
		WorkbenchUtils.showView(AnalysisResultsTableView.ID);
		AnalysisResultsTableView resultTableView = (AnalysisResultsTableView) WorkbenchUtils.getView(AnalysisResultsTableView.ID);
		if(resultTableView != null){
			resultTableView.refresh();
		}
	}
	
	public void showJavaElement(APIDesignAnalysisResult<?> result){
		if(result.getSubject() instanceof IJavaElement)
			JavaUI.revealInEditor(getResultEditor(result, true), (IJavaElement) result.getSubject());
	}
	
	public void showJavaElement(DesignProblem<?> designProblem){
		if(designProblem.getSubject() instanceof IJavaElement)
			JavaUI.revealInEditor(getResultEditor(designProblem, true), (IJavaElement) designProblem.getSubject());
	}
	
	public void rerunAnalysis(DesignProblem<?> designProblem){
		if(designProblem != null)
			rerunAnalysis(designProblem.getAnalysisResult().getRootAnalysisResult());
	}

	public void rerunAnalysis(APIDesignAnalysisResult<?> result){
		if(result != null)
			analyzeElement(result.getRootAnalysisResult().getSubject(), getResultEditor(result, false));
	}
	
	public void rerunAll(){
		for(APIDesignAnalysisResult<?> result : getResults())
			rerunAnalysis(result);
	}
	
	public List<ErrorByType> getDesignProblemsBytype() {
		return designProblemsBytype;
	}

	public void addDesigProblemCount(String type) {
		Boolean exist = false;
		if (designProblemsBytype == null){
			designProblemsBytype = new ArrayList<ErrorByType>();
		}
		for(ErrorByType e : designProblemsBytype){
			if (e.getType().equals(type)){
				e.add();
				exist = true;
			}
		}
		if(!exist){
			ErrorByType errorBytype = new ErrorByType(type, new Integer(1));
			designProblemsBytype.add(errorBytype);
		}
	}
	

}
